#
#	The Makefile implements a basic make utility file for simple to 
#	intermediate assembly, C, C++ or a mixture of these three language(s) 
#	programs. Outputs assembly, intermediate and object files.
#	Copyright (C) 1989-2089 Sergey Sergeevich Tsybanov All Rights Reserved
#
#	The Makefile compilation utility is free software: you can 
#	redistribute it and/or modify it under the terms of the GNU General 
#	Public License as published by the Free Software Foundation, either 
#	version 3 of the License, or (at your option) any later version.
#
#	The Makefile compilation utility is distributed in the hope that it 
#	will be useful, but WITHOUT ANY WARRANTY; without even the implied 
#	warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
#	See the GNU General Public License for more details.
#
#	You should have received a copy of the GNU General Public License 
#	along with the Makefile compilation utility. 
#	If not, see <https://www.gnu.org/licenses/>.
#

#	Shell information
SHELL = /bin/bash

#	Build information
BUILD_TYPE ?= debug
PLATFORM ?= GNU_x86_64

#	Directories
ADIR ?= assembly
BDIR ?= build
EDIR ?= $(BDIR)/$(BUILD_TYPE)
HDIR ?= includes
IDIR ?= intermediates
ODIR ?= objects
SDIR ?= source

#	Assembler
AS = as
ASFLAGS ?=

#	C compiler
CC_GNU_x86_64 ?= gcc
CC_GNU_x86 ?= gcc
CFLAGS_GNU_x86_64_release ?= -O3
CFLAGS_GNU_x86_64_debug ?= -O0 -g -Wall -Wextra
CFLAGS_GNU_x86_release ?= -O3 -m32
CFLAGS_GNU_x86_debug ?= -O0 -m32 -g -Wall -Wextra

CC = $(CC_$(PLATFORM))
CFLAGS ?= --std=c89 $(CFLAGS_$(PLATFORM)_$(BUILD_TYPE))

#	C Preprocessor
CPPFLAGS ?= $(addprefix -I,$(HDIR))

#	C++ Compiler
CXX_GNU_x86_64 ?= g++
CXX_GNU_x86 ?= g++
CXXFLAGS_GNU_x86_64_release ?= -O3
CXXFLAGS_GNU_x86_64_debug ?= -O0 -g -Wall -Wextra
CXXFLAGS_GNU_x86_release ?= -O3 -m32
CXXFLAGS_GNU_x86_debug ?= -O0 -m32 -g -Wall -Wextra

CXX = $(CXX_$(PLATFORM))
CXXFLAGS ?= --std=c++11 $(CFLAGS_$(PLATFORM)_$(BUILD_TYPE))

#	Linker
LD = ld
LDFLAGS ?=
LDLIBS ?=

#	File names, source files and object files
NAMES0 := $(shell find $(SDIR) -name *.s \
| rev | cut -d'/' -f-1 | rev | cut -d'.' -f-1)
NAMES1 := $(shell find $(SDIR) -name *.c \
| rev | cut -d'/' -f-1 | rev | cut -d'.' -f-1)
NAMES2 := $(shell find $(SDIR) -name *.cpp \
| rev | cut -d'/' -f-1 | rev | cut -d'.' -f-1)
OBJSO := $(patsubst %,$(ODIR)/%,$(NAMES0:=.o))
OBJS1 := $(patsubst %,$(ODIR)/%,$(NAMES1:=.o))
OBJS2 := $(patsubst %,$(ODIR)/%,$(NAMES2:=.o))
SRC0 := $(patsubst %,$(SDIR)/%,$(NAMES0:=.s))
SRC1 := $(patsubst %,$(SDIR)/%,$(NAMES1:=.c))
SRC2 := $(patsubst %,$(SDIR)/%,$(NAMES2:=.cpp))

#	Executable
ARGS = 
EXEC ?= hello

#	Various commands
MKDIR := mkdir -p

#	Link C++ specific code?
ifneq ($(SRC2), $(OBJS2))
	LDLIBS += -lstdc++
endif

#	Executable recipes
$(EDIR)/$(EXEC) : $(OBJS0) $(OBJS1) $(OBJS2)
	$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)

#	Assembly recipes
$(OBJS0) : $(SRC0) INIT
	$(foreach names, $(NAMES0), $(AS) $(ASFLAGS) \ 
	$(SDIR)/$(names:=.s) -o $(ODIR)/$(names:=.o);)

#	C recipes
$(OBJS1) : $(SRC1) INIT
	$(foreach names, $(NAMES1), $(CC) $(CFLAGS) $(CPPFLAGS) \
	-E $(SDIR)/$(names:=.c) -o $(IDIR)/$(names:=.i);)
	$(foreach names, $(NAMES1), $(CC) $(CFLAGS) $(CPPFLAGS) \
	-S $(IDIR)/$(names:=.i) -o $(ADIR)/$(names:=.s);)
	$(foreach names, $(NAMES1), $(CC) $(CFLAGS) $(CPPFLAGS) \
	-c $(ADIR)/$(names:=.s) -o $(ODIR)/$(names:=.o);)

#	C++ recipes
$(OBJS2) : $(SRC2) INIT
	$(foreach names, $(NAMES2), $(CXX) $(CXXFLAGS) $(CPPFLAGS) \
	-E $(SDIR)/$(names:=.cpp) -o $(IDIR)/$(names:=.ii);)
	$(foreach names, $(NAMES2), $(CXX) $(CXXFLAGS) $(CPPFLAGS) \
	-S $(IDIR)/$(names:=.ii) -o $(ADIR)/$(names:=.s);)
	$(foreach names, $(NAMES2), $(CXX) $(CXXFLAGS) $(CPPFLAGS) \
	-c $(ADIR)/$(names:=.s) -o $(ODIR)/$(names:=.o);)

#	Initialize build
INIT :
	$(MKDIR) $(ADIR) $(EDIR) $(IDIR) $(ODIR)

.PHONY : clean mostlyclean

clean :
	$(RM) -r $(ADIR) $(BDIR) $(IDIR) $(ODIR) *~

mostlyclean :
	$(RM) -r $(ADIR) $(IDIR) $(ODIR) *~

run :
	@$(MAKE) && ./$(EDIR)/$(EXEC) $(ARGS)